package com.macbulldev.poc.testgin.calculations;

import java.math.BigDecimal;

import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Configurable;
import org.springframework.stereotype.Service;

import com.macbulldev.poc.testgin.domain.LiabilityAmount;
import com.macbulldev.poc.testgin.domain.PolicyForm;
import com.macbulldev.poc.testgin.domain.Scenario;
import com.macbulldev.poc.testgin.domain.Tier;
import com.macbulldev.poc.testgin.domain.TrialCase;
import com.macbulldev.poc.testgin.domain.TrialGroup;
import com.macbulldev.poc.testgin.domain.Underwriter;

@Service("LnBasicCalcs")
@Configurable
public class LnBasicCalcs 
{
	
	private static final Logger LOGGER = Logger.getLogger(LnBasicCalcs.class);
	
	public void generateTestCases(Scenario scenario)
	{
		
		int cnt = 0;
		int tgCnt = 0;
		CalcUtils cu = new CalcUtils();
		
		for(TrialGroup tg : scenario.getTrialGroups())
		{
			tgCnt++;
			cnt = 0;
			for(PolicyForm pf : tg.getPolicyForms())
			{
				for(Underwriter uw : tg.getUnderwriters())
				{
					
					for(Tier tier: tg.getRate().getTiers())
					{
						
						TrialCase tc1 = new TrialCase();//low
						TrialCase tc2 = new TrialCase();//mid
						TrialCase tc3 = new TrialCase();//high
						
						//building test case name
						String tcName1 = "TC_"+scenario.getStateOrProvince().getCode();
						tcName1 = tcName1+"_"+uw.getImportName();
						tcName1 = tcName1+"-"+tg.getVisibility();
						tcName1 = tcName1+"_"+tg.getPropertyType();
						tcName1 = tcName1+"-"+pf.getImportName();
						tcName1 = tcName1+"_"+"low_"+tgCnt+"-"+cnt++;
						tc1.setName(tcName1);
						
						String tcName2 = "TC_"+scenario.getStateOrProvince().getCode();
						tcName2 = tcName2+"_"+uw.getImportName();
						tcName2 = tcName2+"-"+tg.getVisibility();
						tcName2 = tcName2+"_"+tg.getPropertyType();
						tcName2= tcName2+"-"+pf.getImportName();
						tcName2 = tcName2+"_"+"mid_"+tgCnt+"-"+cnt++;
						tc2.setName(tcName2);
						
						String tcName3 = "TC_"+scenario.getStateOrProvince().getCode();
						tcName3 = tcName3+"_"+uw.getImportName();
						tcName3 = tcName3+"-"+tg.getVisibility();
						tcName3 = tcName3+"_"+tg.getPropertyType();
						tcName3= tcName3+"-"+pf.getImportName();
						tcName3 = tcName3+"_"+"high_"+tgCnt+"-"+cnt++;
						tc3.setName(tcName3);
						

						//get liability amounts
						BigDecimal lower = tier.getTierLowerAmt();
						BigDecimal mid = cu.roundDown(tier.getTierUpperAmt().subtract(tier.getTierLowerAmt()).divide(new BigDecimal("2")).add(tier.getTierLowerAmt()));
						BigDecimal upper = tier.getTierUpperAmt();
						
						
						/*
						 * 
						 * If you were going to change the calc steps or the premium based on some variable
						 * like The State it would begin here eg.
						 * 
						 * if(scenario.getStateOrProvince.getDisplayName.equalsIgnorCase("Utah"))
						 * {
						 * 		tc1.setCalcSteps(getBasicCalcStepsForUtah(tier,tg,lower.longValue());
						 * }
						 * 
						 */
						
						//Adding the Calc steps
						tc1.setCalcSteps(getCalcSteps(tier,tg,lower.longValue()));
						tc2.setCalcSteps(getCalcSteps(tier,tg,mid.longValue()));
						tc3.setCalcSteps(getCalcSteps(tier,tg,upper.longValue()));
						

						//adding the premium
						//tc1.setPremium(getPremium(tier,tg,lower.longValue()));
						//tc2.setPremium(getPremium(tier,tg,mid.longValue()));
						//tc3.setPremium(getPremium(tier,tg,upper.longValue()));
						

						tc1.setTrialGroup(tg);
						tc2.setTrialGroup(tg);
						tc3.setTrialGroup(tg);
						
						tc1.persist();
						tc2.persist();
						tc3.persist();
						
						//Add Liability Amounts
						LiabilityAmount la_lower = new LiabilityAmount();
						la_lower.setAmount(lower);
						la_lower.setImportName("AmountLoan1");
						la_lower.setTrialCase(tc1);
						la_lower.setTrialGroup(tg);
						la_lower.setPolicyForm(pf);
						la_lower.setPremium(getPremium(tier,tg,lower.longValue()));
						la_lower.persist();
						
						LiabilityAmount la_mid = new LiabilityAmount();
						la_mid.setAmount(mid);
						la_mid.setImportName("AmountLoan1");
						la_mid.setTrialCase(tc2);
						la_mid.setTrialGroup(tg);
						la_mid.setPolicyForm(pf);
						la_mid.setPremium(getPremium(tier,tg,mid.longValue()));
						la_mid.persist();
						
						LiabilityAmount la_high = new LiabilityAmount();
						la_high.setAmount(upper);
						la_high.setImportName("AmountLoan1");
						la_high.setTrialCase(tc3);
						la_high.setTrialGroup(tg);
						la_high.setPolicyForm(pf);
						la_high.setPremium(getPremium(tier,tg,upper.longValue()));
						la_high.persist();
						
					}//end tiers list
				}//end underwriters list
				
			
			}//end policy form
			
		}//end trial group
	}//end generated calcs
	
	/*
	 * 
	 * method for creating the calc steps in String form
	 * 
	 * 
	 */
	public String getCalcSteps(Tier tier,TrialGroup trialGroup,Long liabilityAmt)
	{
		
		CalcUtils cu = new CalcUtils();
		
		String calcSteps = "";
		String add = tier.getTierAddAmt()+"";
		String subtract = tier.getSubtractAmt()+"";
		String divide = tier.getTierDivideAmt()+"";
		String multiply = tier.getTierMultiplyAmt()+"";
		String additional = trialGroup.getAdditionalCharge()+"";
		String basicRate = trialGroup.getBasicRate()+"";
		
		Long roundAmt = trialGroup.getLiabilityRoundAmt();
		String liability = cu.roundLiabilityAmtUp(liabilityAmt, roundAmt)+"";
		
		//tier structure
		calcSteps = add+" + [("+liability+" - "+subtract+") / "+divide+"] x "+multiply;
		
		//multiliply by basic rate
		calcSteps = "("+calcSteps+") x "+basicRate;
		
		//if additional charge is supplied add it at the end
		if(trialGroup.getAdditionalCharge().compareTo(new BigDecimal("0")) != 0)
		{
			calcSteps = "{"+calcSteps+"} plus additional  "+additional;
		}
		
		BigDecimal premium = getPremium(tier,trialGroup,liabilityAmt);
		
		//check for minimum 
		if(premium.compareTo(trialGroup.getMinimum()) <= 0)
		{
			calcSteps = calcSteps+" minimum  "+premium;
		}
		else
		{
			calcSteps = calcSteps+" = "+premium;
		}
		
		
		//divide cannot be zero unless it is a flat amount
		BigDecimal compare = new BigDecimal("0");
		BigDecimal compare2 = new BigDecimal(divide);
		compare2 = cu.roundUp(compare2);
		
		if(compare.compareTo(compare2) == 0)
		{
			calcSteps = "ONLY! Chuck Norris can divide by zero, and you my friend are not him.  You need to re-evaluate your rate table tier";
		}
		
		//if flat charge
		if(tier.getIsFlatCharge())
		{
			calcSteps = "Flat Rate:  "+tier.getTierAddAmt()+" x "+trialGroup.getBasicRate()+" = "+premium;
		}
		
		
		return calcSteps;
	}
	
	
	/*
	 * 
	 * Method for Retrieving the Basic Premium
	 * 
	 */
	public BigDecimal getPremium(Tier tier,TrialGroup trialGroup,Long liabilityAmt)
	{
		
		CalcUtils cu = new CalcUtils();
		BigDecimal premium = new BigDecimal("0.00");

		BigDecimal add = new BigDecimal(tier.getTierAddAmt()+"");
		BigDecimal subtract = new BigDecimal(tier.getSubtractAmt()+"");
		BigDecimal divide = new BigDecimal(tier.getTierDivideAmt()+"");
		BigDecimal multiply = new BigDecimal(tier.getTierMultiplyAmt()+"");
		
		
		Long roundAmt = trialGroup.getLiabilityRoundAmt();
		BigDecimal liability = new BigDecimal(cu.roundLiabilityAmtUp(liabilityAmt, roundAmt)+"");
		
		//divide cannot be zero
		BigDecimal compare = new BigDecimal("0");
		BigDecimal compare2 = cu.roundUp(divide);
		
		
		//get basic premium for flat charge
		if(tier.getIsFlatCharge())
		{
			premium = add;

			//apply basic rate
			premium = premium.multiply(trialGroup.getBasicRate());

			//apply additional charge
			premium = premium.add(trialGroup.getAdditionalCharge());
			//apply Rounding Rules
			premium = cu.round(premium, trialGroup);
			//check minimum
			if(premium.compareTo(trialGroup.getMinimum()) <= 0)
			{
				premium = trialGroup.getMinimum();
			}
		}
		else if(compare.compareTo(compare2) == 0 && tier.getIsFlatCharge() == false)//divide cannot be zero unless it is a flat amount
		{
			//if divide is zero and the the tier is not a flat charge set the premium to Zero
			premium = new BigDecimal("0.00");
		}
		else
		{
			premium = liability.subtract(subtract);
			premium = premium.divide(divide);
			premium = premium.multiply(multiply);
			premium = premium.add(add);
			//apply basic rate
			premium = premium.multiply(trialGroup.getBasicRate());
			//apply additional charge
			premium = premium.add(trialGroup.getAdditionalCharge());
			//apply Rounding Rules
			premium = cu.round(premium, trialGroup);
			//check minimum
			if(premium.compareTo(trialGroup.getMinimum()) <= 0)
			{
				premium = trialGroup.getMinimum();
			}
		}
		
		return premium;
	}

}
